package org.example.springtest.annotation;

import org.junit.jupiter.api.Test;
import org.junit.platform.commons.util.ReflectionUtils;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;

import java.lang.annotation.*;
import java.lang.reflect.Method;
import java.util.Set;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

/**
 * @author yutianhong
 * @version 1.0
 * @see org.springframework.core.annotation.AnnotatedElementUtils
 * @since 2025/4/3 10:57
 */
public class AnnotatedElementUtilsTest {

    @TestExplicitAliases(a = "TestExplicitAliases")
    @TestImplicitAliases(a = "TestImplicitAliases")
    @TestTransitive(a = "TestTransitiveImplicitAliases")
    @TestImplicitOverrides(a = "TestImplicitOverrides")
    static class GeneralTestClass {
    }

    // 测试显式别名
    @Test
    public void testExplicitAliases() {
        // 显式别名生效
        AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(GeneralTestClass.class, TestExplicitAliases.class);
        assertNotNull(attributes);
        assertEquals("TestExplicitAliases", attributes.getString("a"));
        assertEquals("TestExplicitAliases", attributes.getString("b"));

        TestExplicitAliases annotation = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestExplicitAliases.class);
        assertNotNull(annotation);
        assertEquals("TestExplicitAliases", annotation.a());
        assertEquals("TestExplicitAliases", annotation.b());
    }

    // 测试隐式别名
    @Test
    public void testImplicitAliases() {
        TestImplicitAliases annotation = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestImplicitAliases.class);
        assertNotNull(annotation);
        assertEquals("TestImplicitAliases", annotation.a());
        assertEquals("TestImplicitAliases", annotation.b());
        // 隐式别名生效
        TestImplicitAliasesMetaL1 meta = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestImplicitAliasesMetaL1.class);
        assertNotNull(meta);
        assertEquals("TestImplicitAliases", meta.x());
        // 显式覆盖生效
    }

    // 测试传递隐式别名
    @Test
    public void testTransitiveImplicitAliases() {
        // 传递隐式别名
        TestTransitive annotation = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestTransitive.class);
        assertNotNull(annotation);
        assertEquals("TestTransitiveImplicitAliases", annotation.a());
        assertEquals("TestTransitiveImplicitAliases", annotation.b());

        // 显式覆盖
        AnnotationAttributes metaL1 = AnnotatedElementUtils.getMergedAnnotationAttributes(GeneralTestClass.class, TestTransitiveMetaL1.class);
        assertNotNull(metaL1);
        assertEquals("TestTransitiveImplicitAliases", metaL1.getString("x"));

        // 传递显式覆盖
        TestTransitiveMetaL2 metaL2;
        metaL2 = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestTransitiveMetaL2.class);
        assertNotNull(metaL2); // 支持多层元注解
        assertEquals("TestTransitiveImplicitAliases", metaL2.y());
        metaL2 = AnnotatedElementUtils.findMergedAnnotation(GeneralTestClass.class, TestTransitiveMetaL2.class);
        assertNotNull(metaL2);
        assertEquals("TestTransitiveImplicitAliases", metaL2.y());
    }

    // 测试隐式覆盖
    @Test
    public void testImplicitOverrides() {
        TestImplicitOverrides annotation = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestImplicitOverrides.class);
        assertNotNull(annotation);
        assertEquals("TestImplicitOverrides", annotation.a());

        TestImplicitOverridesMetaL1 metaL1 = AnnotatedElementUtils.getMergedAnnotation(GeneralTestClass.class, TestImplicitOverridesMetaL1.class);
        assertNotNull(metaL1);
        assertEquals("TestImplicitOverrides", metaL1.a());
    }

    // 测试获取元注解
    @Test
    void testGetMetaAnnotationTypes() {
        Set<String> metaAnnotations = AnnotatedElementUtils.getMetaAnnotationTypes(
                GeneralTestClass.class, TestTransitive.class);
        assertThat(metaAnnotations)
                .containsExactly(TestTransitiveMetaL1.class.getName(), TestTransitiveMetaL2.class.getName());
    }

    // 测试判断元注解是否存在
    @Test
    void testHasMetaAnnotationTypes() {
        boolean hasMeta = AnnotatedElementUtils.hasMetaAnnotationTypes(
                GeneralTestClass.class, TestTransitiveMetaL2.class.getName());
        assertTrue(hasMeta);
    }

    // ====================================================================================================
    // ====================================================================================================

    // 测试元注解
    @Retention(RetentionPolicy.RUNTIME)
    @interface MetaAnnotation {
    }

    // 组合注解，带有元注解
    @Retention(RetentionPolicy.RUNTIME)
    @MetaAnnotation
    @interface ComposedAnnotation {
        String value() default "default";
    }

    // 可重复注解及其容器
    @Retention(RetentionPolicy.RUNTIME)
    @Repeatable(RepeatableContainer.class)
    @interface RepeatableAnnotation {
        String value();
    }

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface RepeatableContainer {
        RepeatableAnnotation[] value();
    }

    // 测试继承的注解
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @Inherited
    @interface InheritedAnnotation {
    }

    @InheritedAnnotation
    static class Parent {
    }

    static class Child extends Parent {
    }

    // 测试方法级别的注解
    static class MethodLevel {
        @SuppressWarnings("unused") // used by java reflection
        @ComposedAnnotation("methodValue")
        public void annotatedMethod() {
        }
    }

    @Test
    void testIsAnnotated() {
        boolean isAnnotated = AnnotatedElementUtils.isAnnotated(MethodLevel.class, ComposedAnnotation.class);
        assertFalse(isAnnotated); // 类上无注解

        Method method = ReflectionUtils.findMethod(MethodLevel.class, "annotatedMethod").orElse(null);
        assertNotNull(method);
        isAnnotated = AnnotatedElementUtils.isAnnotated(method, ComposedAnnotation.class);
        assertTrue(isAnnotated);
        isAnnotated = AnnotatedElementUtils.isAnnotated(method, MetaAnnotation.class);
        assertTrue(isAnnotated);
    }

    @Test
    void testGetMergedRepeatableAnnotations() {
        @RepeatableAnnotation("a")
        @RepeatableAnnotation("b")
        class TestClass {
        }

        Set<RepeatableAnnotation> annotations = AnnotatedElementUtils.getMergedRepeatableAnnotations(
                TestClass.class, RepeatableAnnotation.class);
        assertThat(annotations).extracting("value").containsExactly("a", "b");
    }

    @Test
    void testFindAllMergedAnnotations() {
        Set<InheritedAnnotation> annotations = AnnotatedElementUtils.findAllMergedAnnotations(
                Child.class, InheritedAnnotation.class);
        assertThat(annotations).hasSize(1);
    }

    @Test
    void getAllMergedAnnotations() {
        @ComposedAnnotation
        class TestClass {
        }

        Set<ComposedAnnotation> annotations = AnnotatedElementUtils.getAllMergedAnnotations(
                TestClass.class, ComposedAnnotation.class);
        assertThat(annotations).hasSize(1);
    }

    // ====================================================================================================
    // ====================================================================================================

    @Retention(RetentionPolicy.RUNTIME)
    @interface TestAnnotation {
        String value();
    }

    // 桥接方法测试
    interface Generic<T> {
        @TestAnnotation("interface-method")
        @MetaAnnotation
        @ComposedAnnotation("interface-method")
        void process(T t);
    }

    static class StringProcessor implements Generic<String> {
        @Override
        @ComposedAnnotation("implement-method")
        public void process(String s) {
        }
    }

    // 桥接方法处理测试
    @Test
    void testGetMergedAnnotation_HandleBridgeMethod() throws Exception {
        Method bridgeMethod = StringProcessor.class.getMethod("process", Object.class);
        TestAnnotation ann = AnnotatedElementUtils.getMergedAnnotation(bridgeMethod, TestAnnotation.class);
        assertNull(ann); // 不支持桥接方法
        ann = AnnotatedElementUtils.findMergedAnnotation(bridgeMethod, TestAnnotation.class);
        assertNotNull(ann);
        assertEquals("interface-method", ann.value()); // 查找接口方法上的注解

        ComposedAnnotation annotation = AnnotatedElementUtils.getMergedAnnotation(bridgeMethod, ComposedAnnotation.class);
        assertNotNull(annotation);
        assertEquals("implement-method", annotation.value());   // 优先获取当前实现类上的注解

        MetaAnnotation metaAnnotation = AnnotatedElementUtils.getMergedAnnotation(bridgeMethod, MetaAnnotation.class);
        assertNotNull(metaAnnotation);
    }

}
